<template>
  <el-dialog
    :visible.sync="dialogVisible"
    width="600px"
    :before-close="handleClose"
    :close-on-click-modal="false">
    <span slot="title" style="color:#303753;font-size:14px;font-weight:600">发送图片</span>
    <div style="text-align: center; margin-top: -20px">
      <el-alert v-if="!loading" :title="`是否发送${imgSrc.length > 1 ? ('以下' + imgSrc.length + '张') : '该'}图片?`" type="warning" show-icon :closable="false"></el-alert>
      <div class="imgDiv" v-if="imgSrc.length === 1">
        <img :src="imgSrc[0].blobUrl"/>
        <div class="loading" v-if="imgSrc[0].status === 'upload'">{{ imgSrc[0].percent }}%</div>
        <div class="loading error" v-if="imgSrc[0].status === 'error'" @click="upload(imgSrc[0], 0)">重新上传</div>
      </div>
      <draggable v-model="imgSrc" animation="150" ghostClass="dragClass" class="imgDiv" v-if="imgSrc.length > 1" filter=".notmove">
        <div class="imgContent" v-for="(item,index) in imgSrc" :key="index" :class="{'notmove': item.status === 'upload'}">
          <img :src="item.blobUrl" object-fit="scale-down" width="100%"/>
          <div class="close" v-if="item.status !== 'upload'" @click="deleteImg(index)"><i style="font-size:12px" class="V7_iconfont V7_iconV7tubiao_guanbi"></i></div>
          <div class="loading" v-if="item.status === 'upload'">{{ item.percent }}%</div>
          <div class="loading error" v-if="item.status === 'error'" @click="upload(item, index)">重新上传</div>
        </div>
      </draggable>
    </div>
    <span slot="footer" class="dialog-footer">
      <el-button @click="handleClose">取 消</el-button>
      <el-button type="primary" @click="submit" :loading="loading">确 定</el-button>
    </span>
  </el-dialog>
</template>

<script lang="ts">
import { Component, Vue, Prop, Watch } from 'vue-property-decorator';
import { uploadQiniu } from '@/utils/upload'
import draggable from 'vuedraggable'
const imgList = ['png', 'jpg', 'gif', 'jpeg', 'bmp']
const fileList = ['txt', 'xlsx', 'xls', 'docx', 'doc', 'pdf', 'ppt', 'pptx', 'key', 'mp3', 'wave', 'mpeg', 'midi', 'mp4', 'zip']
/**
 * 输入框粘贴剪切板图片组件
 *
 * 1、根据传入渠道，设置不同的最大上传大小
 *
 * 2、支持多个图片的粘贴和拖拽
 *
 * 3、点击确定按钮，进行文件的上传，任一文件上传失败，可以重新上传，也可以忽略失败文件直接发送
 *
 * 4、支持图片类型['png', 'jpg', 'gif', 'jpeg', 'bmp']
 * @Prop webchatInfo 消息渠道
 */
@Component({
  name: 'PasteImg',
  components: { draggable }
})
export default class PasteImg extends Vue {
  [x: string]: any
  @Prop() private webchatInfo: any
  private imgSrc: any = []
  private dialogVisible: boolean = false
  private imgIndex: number = 0
  private maxSize = 10 // 图片最大大小
  private loading: boolean = false
  private firstError: boolean = true // 第一次上传出现错误提示，后面不在提示
  private uploadList: any = [] // 保存上传实例，销毁时使用

  @Watch('webchatInfo', { immediate: true })
  private webchatInfoChange (val) {
    if (['wxkf', 'wxwork'].includes(val.platform)) { // 微信客服和企业微信渠道
      this.maxSize = 10
    } else {
      this.maxSize = 10
    }
  }

  private mounted () {
    this.addPasteEvent()
  }

  private activated () {
    this.addPasteEvent()
  }

  private destroyed () {
    this.removePasteEvent()
  }

  private handleClose () {
    this.uploadList.forEach(element => {
      try {
        element.then(res => res.unsubscribe())
      } catch (e) {
        console.log('取消上传')
      }
    });
    this.imgSrc = []
    this.dialogVisible = false
    this.loading = false
    this.firstError = true
  }

  private deleteImg (index) {
    this.imgSrc.splice(index, 1)
  }

  /**
   * 上传单个图片
   * status: upload(上传中) error(上传失败) ok(上传完成)
   */
  private upload (item: any, index: number) {
    if (item.status === 'ok') {
      return
    }
    this.$set(this.imgSrc[index], 'status', 'upload')
    const request = {
      file: item.blob,
      onError: () => {
        this.$set(this.imgSrc[index], 'status', 'error')
        this.$set(this.imgSrc[index], 'percent', 0)
        this.sendMsg()
      },
      onProgress: ({ percent }) => {
        this.$set(this.imgSrc[index], 'percent', Math.floor(percent))
      },
      onSuccess: (data: any) => {
        if (data.key) {
          this.$set(this.imgSrc[index], 'url', data.key)
          this.$set(this.imgSrc[index], 'status', 'ok')
          this.sendMsg()
        } else {
          return false
        }
      }
    }
    const subscription = uploadQiniu({ type: 'im', fileName: 'clipboard' + new Date().getTime() + '.png' }, request)
    this.uploadList.push(subscription)
  }

  // 发送消息
  private submit () {
    this.loading = true
    this.imgSrc.forEach(async (v, index) => {
      this.upload(v, index)
    })
  }

  // 轮询检测图片是否上传完成，执行发送消息步骤
  private sendMsg () {
    if (this.imgSrc.some(v => v.status === 'upload')) {
      return
    }
    if (this.firstError && this.imgSrc.some(v => v.status === 'error')) {
      this.firstError = false
      this.loading = false
      return
    }
    this.imgSrc.forEach(v => {
      if (v.status === 'ok') {
        (this.$parent as any).uploadImgSuccess({ key: v.url })
        URL.revokeObjectURL(v.blobUrl)
      }
    })
    this.handleClose()
  }

  private addPasteEvent () {
    if (document.getElementById('webchatSendMsg')) {
      (document.getElementById('webchatSendMsg') as any).addEventListener('paste', this.pasteFunction);
      (document.getElementById('webchatSendMsg') as any).addEventListener('drop', this.dropFunction)
    }
  }

  private removePasteEvent () {
    if (document.getElementById('webchatSendMsg')) {
      (document.getElementById('webchatSendMsg') as any).removeEventListener('paste', this.pasteFunction);
      (document.getElementById('webchatSendMsg') as any).removeEventListener('drop', this.dropFunction)
    }
  }

  // 图片粘贴事件
  private pasteFunction (event: any, successFn: any) {
    this.imgSrc = []
    const items = (event.clipboardData || event.originalEvent.clipboardData).items
    const files = (event.clipboardData || event.originalEvent.clipboardData).files
    // 区分纯图片上传和文件图片混合上传，使用不同的处理方式
    if ([...files].some(v => {
      const fileName = v.name
      const ext = fileName.split('.').pop()?.toLowerCase()
      if (fileList.includes(ext)) {
        return true
      }
      return false
    })) {
      return false
    }
    [...items].map(v => {
      const type = v.type.split('/').pop()
      if (v.kind === 'file' && imgList.includes(type)) {
        const blob = v.getAsFile()
        if (blob.size > this.maxSize * 1024 * 1024) {
          return false
        }
        const url = URL.createObjectURL(blob)
        this.imgSrc.push({
          blob,
          blobUrl: url,
          percent: 0,
          url: '',
          status: ''
        })
        this.dialogVisible = true
      }
    })
    if (this.dialogVisible) {
      return false
    }
  }

  // 文件拖放事件，只处理图片的情况
  private dropFunction (event: any) {
    event.preventDefault()
    this.imgSrc = []
    const items = [...event.dataTransfer.files]
    // 区分纯图片上传和文件图片混合上传，使用不同的处理方式
    let other = false
    const list: any = []
    items.forEach(v => {
      const fileName = v.name
      const size = v.size / 1024 / 1024
      const ext = fileName.split('.').pop()?.toLowerCase()
      if (imgList.includes(ext)) {
        if (size <= this.maxSize) {
          list.push({ file: v, size })
        }
      }
      if (fileList.includes(ext)) {
        other = true
      }
    })
    if (other) {
      return false
    }
    if (list.length > 0) {
      list.map(v => {
        const reader = new FileReader()
        reader.readAsDataURL(v.file)
        reader.onload = () => {
          this.imgSrc.push({
            blob: v.file,
            blobUrl: reader.result, // 使用blobUrl响应快，图片drop之后可以直接预览
            percent: 0,
            url: '',
            status: ''
          })
        }
      })
      this.dialogVisible = true
    }
  }
}
</script>
<style lang="stylus" scoped>
.progressDiv
  height: 15px
  margin-top: -8px
.imgDiv
  max-width 100%
  height 345px
  margin-top 16px
  position relative
  overflow: auto
  .loading
    width: 100%
    height: 100%
    position: absolute
    top 0px
    left 0px
    text-align: center
    color #fff
    font-size: 14px
    background: rgba(0,0,0,.3)
    line-height: 400px
  .dragClass
    opacity 0
  img
    height: 100%;
    width: 100%;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    object-fit: scale-down;
  .imgContent
    width: 100px
    height: 100px
    float: left
    margin: 0px 11px 11px 0px
    position: relative
    border: 1px solid #EDEDED
    border-radius: 4px
    cursor move
    position: relative
    .loading
      width: 100%
      height: 100%
      position: absolute
      top 0px
      left 0px
      text-align: center
      color #fff
      font-size: 14px
      background: rgba(0,0,0,.3)
      line-height: 100px
    .error
      background: rgba(255,0,0,.2)
    .close
      position: absolute
      width: 12px
      height: 13px
      right: 2px
      top 2px
      color red
      cursor pointer
      display none
  .imgContent:nth-of-type(5n)
    margin-right: 0px
  .imgContent:hover
    .close
      display: block
</style>
